/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 *	            File : 	crcmodel.cpp
 *       Description :  Параметрическая модельная реализация CRC-алгоритма.
 *
 *						Реализовано на основе материала:
 *						Ross N. Williams - Элементарное руководство по
 *						CRC алгоритмам обнаружения ошибок
 *
 *            Create :  28.02.2013
 *            Author :  Golovchenko Aleksey (lexgolovchenko@gmail.com)
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <iomanip>
#include "crcmodel.h"

std::ostream & operator << (std::ostream &out, const CRC_param &p) {

	out << "Width   : " << p.width << "\n";
	out << "Poly    : " << "0x" << std::setw(p.width/4) << std::hex << std::uppercase << std::setfill('0') << p.poly << "\n";
	out << "Init    : " << "0x" << std::setw(p.width/4) << std::hex << std::uppercase << std::setfill('0') << p.init << "\n";
	out << "Xorout  : " << "0x" << std::setw(p.width/4) << std::hex << std::uppercase << std::setfill('0') << p.xorout << "\n";
	out << "Refin   : " << std::boolalpha << p.refin <<  "\n";
    out << "Refout  : " << std::boolalpha << p.refout <<  std::endl;

	return out;
}

CRC_model::CRC_model(const CRC_param &param)
: m_param(param) {

	// установка в начальное состояние
	reset();
}

/*
 * Сброс алгоритма в начальное состояние
 */
void CRC_model::reset() {
	m_CRC = m_param.init;
}

/*
 * Обработка очередного байта, вычесленное значение CRC
 * сохраняется в m_CRC
 */
void CRC_model::process_byte(int byte) {

	unsigned long ubyte = static_cast<unsigned long>(byte) & 0x000000FF;
	unsigned long topbit = (1 << (m_param.width - 1));

	// если алгоритм "зеркальный", то отражаем байт
	if (m_param.refin) {
		ubyte = reflect(ubyte, 8);
	}

	// XOR входного байта со старшим байтом решистра
	m_CRC ^= (ubyte << (m_param.width - 8));

	// XOR с полиномом с раздичными сдвигами (по сути, прямой, не табличный алгоритм)
	for (int i = 0; i < 8; ++i) {
		// проверка выдвигаемого бита, если 1, то делаем XOR с полиномом
		if (m_CRC & topbit) {
			m_CRC = (m_CRC << 1) ^ m_param.poly;
		}
		else {
			m_CRC <<= 1;
		}

		// обнуляем не используемую часть регистра, в зависимости от степени полинома
		if (m_param.width < 32) {
			m_CRC &= ((1 << m_param.width) - 1);
		}
	}
}

/*
 * Обработка блока байтов заданного размера
 */
void CRC_model::process_msg(void *data, int size) {

	unsigned char *buf = reinterpret_cast<unsigned char *>(data);

	// обработка всех байтов по очереди
	for (int i = 0; i < size; ++i) {
		process_byte(*buf++);
	}
}

/*
 * Промежуточное значение регистра CRC
 */
unsigned long CRC_model::get_CRC_reg() {
	return m_CRC;
}

/*
 * Результат алгоритма
 */
unsigned long CRC_model::get_CRC() {

	unsigned long CRCres;

	// если алгоритм "зеркальный", то отражаем результат
	if (m_param.refout) {
		CRCres = m_param.xorout ^ reflect(m_CRC, m_param.width);
	}
	else {
		CRCres = m_param.xorout ^ m_CRC;
	}

	return CRCres;
}

/*
 * Вычисление i-го элемента CRC таблицы, i = [0,255]
 */
unsigned long CRC_model::get_table_elem(int i) {

	unsigned long idx = static_cast<unsigned long>(i) & 0x000000FF;
	unsigned long topbit = (1 << (m_param.width - 1));
	unsigned long res = 0;

	// если алгоритм "зеркальный", то отражаем индекс
	if (m_param.refin) {
		idx = reflect(idx, 8);
	}

	// XOR с полиномом с раздичными сдвигами (по сути, прямой, не табличный алгоритм)
	res = idx << (m_param.width - 8);
	for (int j = 0; j < 8; ++j) {
		if (res & topbit) {
			res = (res << 1) ^ m_param.poly;
		}
		else {
			res <<= 1;
		}
	}

	// если алгоритм "зеркальный", то отражаем полученное таблично значение
	if (m_param.refin) {
		res = reflect(res, m_param.width);
	}

	// обнуляем не используемую часть регистра, в зависимости от степени полинома
	if (m_param.width < 32) {
		res &= ((1 << m_param.width) - 1);
	}

	return res;
}

/*
 * Функиця возвращает значение с b отраженными
 * младшими битами
 */
unsigned long CRC_model::reflect(unsigned long val, const int b) {

	unsigned long ref = val;

	for (int i = 0; i < b; ++i) {
		if (val & (1 << i)) {
			ref |= (1 << ((b - 1) - i));
		}
		else {
			ref &= ~(1 << ((b - 1) - i));
		}
	}

	return ref;
}
